// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_TRANSACTION_H_
#define CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_TRANSACTION_H_

#include <map>
#include <memory>
#include <set>
#include <string>

#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/strings/string_piece.h"
#include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
#include "content/browser/indexed_db/leveldb/leveldb_database.h"
#include "content/browser/indexed_db/leveldb/leveldb_iterator.h"

namespace content {

class LevelDBWriteBatch;

class CONTENT_EXPORT LevelDBTransaction
    : public base::RefCounted<LevelDBTransaction> {
public:
    void Put(const base::StringPiece& key, std::string* value);

    // Returns true if this operation performs a change, where the value wasn't
    // already deleted.
    bool Remove(const base::StringPiece& key);
    virtual leveldb::Status Get(const base::StringPiece& key,
        std::string* value,
        bool* found);
    virtual leveldb::Status Commit();
    void Rollback();

    std::unique_ptr<LevelDBIterator> CreateIterator();

protected:
    virtual ~LevelDBTransaction();
    explicit LevelDBTransaction(LevelDBDatabase* db);
    friend class IndexedDBClassFactory;

private:
    friend class base::RefCounted<LevelDBTransaction>;
    FRIEND_TEST_ALL_PREFIXES(LevelDBDatabaseTest, Transaction);
    FRIEND_TEST_ALL_PREFIXES(LevelDBDatabaseTest, TransactionCommitTest);
    FRIEND_TEST_ALL_PREFIXES(LevelDBDatabaseTest, TransactionIterator);

    struct Record {
        Record();
        ~Record();
        std::string key;
        std::string value;
        bool deleted;
    };

    class Comparator {
    public:
        explicit Comparator(const LevelDBComparator* comparator)
            : comparator_(comparator)
        {
        }
        bool operator()(const base::StringPiece& a,
            const base::StringPiece& b) const
        {
            return comparator_->Compare(a, b) < 0;
        }

    private:
        const LevelDBComparator* comparator_;
    };

    typedef std::map<base::StringPiece, std::unique_ptr<Record>, Comparator>
        DataType;

    class DataIterator : public LevelDBIterator {
    public:
        static std::unique_ptr<DataIterator> Create(
            LevelDBTransaction* transaction);
        ~DataIterator() override;

        bool IsValid() const override;
        leveldb::Status SeekToLast() override;
        leveldb::Status Seek(const base::StringPiece& slice) override;
        leveldb::Status Next() override;
        leveldb::Status Prev() override;
        base::StringPiece Key() const override;
        base::StringPiece Value() const override;
        bool IsDeleted() const;

    private:
        explicit DataIterator(LevelDBTransaction* transaction);
        DataType* data_;
        DataType::iterator iterator_;

        DISALLOW_COPY_AND_ASSIGN(DataIterator);
    };

    class TransactionIterator : public LevelDBIterator {
    public:
        ~TransactionIterator() override;
        static std::unique_ptr<TransactionIterator> Create(
            scoped_refptr<LevelDBTransaction> transaction);

        bool IsValid() const override;
        leveldb::Status SeekToLast() override;
        leveldb::Status Seek(const base::StringPiece& target) override;
        leveldb::Status Next() override;
        leveldb::Status Prev() override;
        base::StringPiece Key() const override;
        base::StringPiece Value() const override;
        void DataChanged();

    private:
        enum Direction { FORWARD,
            REVERSE };

        explicit TransactionIterator(scoped_refptr<LevelDBTransaction> transaction);
        void HandleConflictsAndDeletes();
        void SetCurrentIteratorToSmallestKey();
        void SetCurrentIteratorToLargestKey();
        void RefreshDataIterator() const;
        bool DataIteratorIsLower() const;
        bool DataIteratorIsHigher() const;

        scoped_refptr<LevelDBTransaction> transaction_;
        const LevelDBComparator* comparator_;
        mutable std::unique_ptr<DataIterator> data_iterator_;
        std::unique_ptr<LevelDBIterator> db_iterator_;
        LevelDBIterator* current_;

        Direction direction_;
        mutable bool data_changed_;

        DISALLOW_COPY_AND_ASSIGN(TransactionIterator);
    };
    // Returns true if the key was originally marked deleted, false otherwise.
    bool Set(const base::StringPiece& key, std::string* value, bool deleted);
    void RegisterIterator(TransactionIterator* iterator);
    void UnregisterIterator(TransactionIterator* iterator);
    void NotifyIterators();

    LevelDBDatabase* db_;
    const LevelDBSnapshot snapshot_;
    const LevelDBComparator* comparator_;
    Comparator data_comparator_;
    DataType data_;
    bool finished_;
    std::set<TransactionIterator*> iterators_;

    DISALLOW_COPY_AND_ASSIGN(LevelDBTransaction);
};

// Reads go straight to the database, ignoring any writes cached in
// write_batch_, and writes are write-through, without consolidation.
class LevelDBDirectTransaction {
public:
    static std::unique_ptr<LevelDBDirectTransaction> Create(LevelDBDatabase* db);

    ~LevelDBDirectTransaction();
    void Put(const base::StringPiece& key, const std::string* value);
    leveldb::Status Get(const base::StringPiece& key,
        std::string* value,
        bool* found);
    void Remove(const base::StringPiece& key);
    leveldb::Status Commit();

private:
    explicit LevelDBDirectTransaction(LevelDBDatabase* db);

    LevelDBDatabase* db_;
    std::unique_ptr<LevelDBWriteBatch> write_batch_;
    bool finished_;

    DISALLOW_COPY_AND_ASSIGN(LevelDBDirectTransaction);
};

} // namespace content

#endif // CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_TRANSACTION_H_
